package com.hup.joystick.view.functionSetting;

import com.hup.joystick.constant.FunctionConstant.FunctionType;
import com.hup.joystick.model.DeviceConfig;
import com.hup.joystick.model.FunctionConfig;
import com.hup.joystick.model.FunctionConfig.FunctionItem;
import com.hup.joystick.model.KeyMapConfig;
import com.hup.joystick.model.MainConfig;
import com.hup.joystick.model.joystick.NativeEvent;
import com.hup.joystick.model.joystick.StandardEvent;
import com.hup.joystick.mvp.FunctionSettingMvp.FunctionSettingPresenter;
import com.hup.joystick.service.joystick.AutoConnectListener;
import com.hup.joystick.view.functionSetting.FunctionSettingItem.FunctionEditListener;
import com.hup.joystick.view.main.FunctionPreviewItem;
import com.hup.utils.commons.DateBuilder;
import com.hup.utils.commons.TextUtil;
import com.hup.utils.commons.exception.AppCommonException;
import com.hup.utils.commons.start.DialogHelper;
import com.hup.utils.commons.start.LookThemeUtil;
import com.hup.utils.commons.start.SpringContext;
import com.hup.utils.swing.ComponentUtil;
import com.hup.utils.swing.customView.BaseFileChooser.ChoseListener;
import com.hup.utils.swing.customView.FileChooserPanel;
import com.hup.utils.swing.frame.BaseFrame;
import com.hup.utils.swing.frame.SingletonWindowHelper;
import lombok.extern.log4j.Log4j2;

import javax.swing.DefaultComboBoxModel;
import javax.swing.JOptionPane;
import java.awt.GridLayout;
import java.awt.event.WindowEvent;
import java.io.File;
import java.util.ArrayList;
import java.util.Date;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

/**
 * @author hugan
 * @date 2020/10/25
 */
@Log4j2
public class FunctionSettingFrame extends BaseFrame {
    public static final String TITLE = "功能配置";

    private final FunctionSettingPresenter presenter;
    private final ViewItemEditHandler viewItemEditHandler = new ViewItemEditHandler();
    private FunctionSettingForm form;
    /**
     * 是否修改过配置
     */
    private boolean configNotSave;
    private FunctionConfig lastSelectedConfig;

    public static void showFrame(FunctionPreviewItem item) {
        FunctionSettingFrame window = SingletonWindowHelper.getWindow(FunctionSettingFrame.class);
        FunctionConfig initTargetConfig = item == null ? null : item.getFunction();
        if (window == null) {
            window = new FunctionSettingFrame(initTargetConfig);
        } else {
            window.initModel(initTargetConfig);
        }
        window.setVisible(true);
    }

    private FunctionSettingFrame(FunctionConfig initTargetConfig) {
        super(TITLE);
        presenter = SpringContext.getBean(FunctionSettingPresenter.class);

        initView();
        initListener();
        initModel(initTargetConfig);
        connectDevice();
    }

    private void initView() {
        form = new FunctionSettingForm();
        setContentPane(form.root);

        form.cbConfig.setItemTextHandler(config -> config.name);

        form.pApp.setLayout(new GridLayout(1, 1));
        form.pFunction.setLayout(new GridLayout(1, 1));
        ((FileChooserPanel) form.pApp.getComponent(0)).setChoseListener(viewItemEditHandler);
        ((FunctionSettingItem) form.pFunction.getComponent(0)).setEditListener(viewItemEditHandler);
        form.btnSave.setEnabled(false);

        LookThemeUtil.initFrameSize(this, 800, 500);
        setLocationRelativeTo(null);
    }

    private void initListener() {
        form.cbConfig.addActionListener(e -> viewItemEditHandler.onSelectedConfig(form.cbConfig.getSelectedItem()));
        form.btnDelete.setActionListener(confirm("删除配置", () -> {
            FunctionConfig config = form.cbConfig.getSelectedItem();
            //noinspection ConstantConditions
            File file = new File(config.configPath());
            if (file.exists() && !file.delete()) log.warn("删除错误={}", file);
            switchConfigEdited(false);
            initModel(null);
        }));
        form.btnAddConfig.setActionListener(e -> {
            if (confirmNotSaveConfig()) return;

            FunctionConfig config = FunctionConfig.getBuildInFunction();
            StringBuilder sb = new StringBuilder(config.name);//处理编码警告
            while (hadSameConfigName(config)) {//不允许配置名重复
                sb.append(DateBuilder.yyMMdd_HHmmss(new Date()));
                config.name = sb.toString();
            }
            DefaultComboBoxModel<FunctionConfig> model = (DefaultComboBoxModel<FunctionConfig>) form.cbConfig.getModel();
            model.addElement(config);
            model.setSelectedItem(config);
            switchConfigEdited(true);
        });

        form.cbExclusive.addActionListener(e -> switchConfigEdited(true));
        form.btnRename.addActionListener(e -> {
            String oldName = lastSelectedConfig.name;
            File oldFile = new File(lastSelectedConfig.configPath());
            boolean sameName = false;
            do {//不允许配置名重复
                String input = (String) JOptionPane.showInputDialog(this, sameName ? "不能重名" : null, "输入配置名",
                        JOptionPane.INFORMATION_MESSAGE, null, null, lastSelectedConfig.name);
                if (TextUtil.isBlank(input)) {
                    lastSelectedConfig.name = oldName;
                    return;
                }
                log.debug("input={}", input);
                lastSelectedConfig.name = input;
                sameName = hadSameConfigName(lastSelectedConfig);
            } while (sameName);
            if (oldName.equals(lastSelectedConfig.name)) return;

            if (oldFile.exists()) {//有配置文件,马上保存
                //noinspection ResultOfMethodCallIgnored
                oldFile.delete();
                lastSelectedConfig.save();
                switchConfigEdited(false);
            } else {//无配置文件,不保存
                switchConfigEdited(true);
            }
            form.cbConfig.repaint();
        });
        form.btnClearFunctionItem.addActionListener(confirm("清空功能列表", () -> {
            if (!lastSelectedConfig.lstFunction.isEmpty()) {
                lastSelectedConfig.lstFunction.clear();
                viewItemEditHandler.initViewByConfig(lastSelectedConfig);
                switchConfigEdited(true);
            }
        }));
        form.btnSave.addActionListener(e -> runWithCatch("保存配置", this::saveConfig));
        form.btnCancel.addActionListener(e -> {
            boolean close = onWindowClosing(null);
            if (close) dispose();
        });
    }

    private void initModel(FunctionConfig initTargetConfig) {
        execute("加载配置", () -> {
            ArrayList<FunctionConfig> userFunctions = FunctionConfig.getUserFunctions();
            MainConfig mainConfig = new MainConfig().loadFromFile();
            //根据mainConfig 排序userFunctions
            //创建一个Map 用于快速查找name对应的索引
            Map<String, Integer> nameToIndex = IntStream.range(0, mainConfig.lstFunctionRecord.size())
                    .boxed()
                    .collect(Collectors.toMap(i -> mainConfig.lstFunctionRecord.get(i).getName(), i -> i));
            //排序
            userFunctions.sort((o1, o2) -> {
                int index1 = nameToIndex.getOrDefault(o1.getName(), -1);
                int index2 = nameToIndex.getOrDefault(o2.getName(), -1);
                return index1 - index2;
            });
            return userFunctions;
        }, configs -> {
            form.cbConfig.setModel(configs);
            if (lastSelectedConfig != null) {
                //刷新了Model后,要重新找到对应的对象
                lastSelectedConfig = configs.stream()
                        .filter(config -> Objects.equals(config.name, lastSelectedConfig.getName()))
                        .findFirst()
                        .orElse(null);
            }

            //根据initTargetConfig,选中对应配置
            FunctionConfig selectConfig = null;
            if (initTargetConfig != null) {
                selectConfig = configs.stream()
                        .filter(config -> Objects.equals(config.name, initTargetConfig.getName()))
                        .findFirst()
                        .orElse(null);
            }
            if (selectConfig == null) {
                viewItemEditHandler.onSelectedConfig(configs.get(0));//不修改JComboBox
            } else {
                form.cbConfig.setSelectedItem(selectConfig);//修改JComboBox,触发监听
            }
        });
    }

    @Override
    public boolean onWindowClosing(WindowEvent e) {
        if (confirmNotSaveConfig()) return false;
        presenter.onWindowClosing();
        return true;
    }

    private void switchConfigEdited(boolean edited) {
        if (lastSelectedConfig != null) {//有设备时才修改
            configNotSave = edited;
            form.btnSave.setEnabled(edited);
        }
    }

    /**
     * 判断有无修改配置,如果有则弹窗确认
     *
     * @return true-取消进行中的操作(不放弃保存);false-放弃保存(然后取消已编辑状态)
     */
    private boolean confirmNotSaveConfig() {
        //没有配置或没有修改配置:不取消
        if (lastSelectedConfig == null || !configNotSave) return false;
        //不是确认取消:不取消
        boolean notSave = DialogHelper.yesNo("放弃保存?").show(this);
        if (notSave) switchConfigEdited(false);
        return !notSave;
    }

    private void saveConfig() {
        lastSelectedConfig.exclusive = form.cbExclusive.isSelected();
        lastSelectedConfig.lstApp.clear();
        for (int i = 0, count = form.pApp.getComponentCount(); i < count; i++) {
            String path = ((FileChooserPanel) form.pApp.getComponent(i)).getSelectedFilePath();
            if (path != null) lastSelectedConfig.lstApp.add(path);
        }
        if (lastSelectedConfig.exclusive && lastSelectedConfig.lstApp.isEmpty()) {
            throw new AppCommonException("独占模式下,需要添加[生效程序]");
        }

        lastSelectedConfig.lstFunction.clear();
        for (int i = 0, count = form.pFunction.getComponentCount(); i < count; i++) {
            FunctionSettingItem item = (FunctionSettingItem) form.pFunction.getComponent(i);
            lastSelectedConfig.viewToFunction(item);
        }
        lastSelectedConfig.sortConfigFields();//排序
        lastSelectedConfig.save();

        switchConfigEdited(false);
        form.cbConfig.setSelectedItem(lastSelectedConfig);//触发排序
    }

    private boolean hadSameConfigName(FunctionConfig config) {
        DefaultComboBoxModel<FunctionConfig> model = (DefaultComboBoxModel<FunctionConfig>) form.cbConfig.getModel();
        for (int i = model.getSize() - 1; i >= 0; i--) {
            FunctionConfig elementAt = model.getElementAt(i);
            if (elementAt != config && Objects.equals(elementAt.name, config.name)) return true;
        }
        return false;
    }

    private void connectDevice() {
        log.info("");
        presenter.startAutoConnect(new AutoConnectListener() {
            private DeviceConfig currentDeviceConfig;

            @Override
            public void onScanning() {
                form.btnDeviceState.onScanning();
            }

            @Override
            public void beforeConnect(DeviceConfig deviceConfig) {
                log.info("device={}", deviceConfig.name);
                currentDeviceConfig = deviceConfig;
                form.btnDeviceState.onConnected(deviceConfig.name);
            }

            @Override
            public KeyMapConfig getKeyMapConfig() {
                return currentDeviceConfig.keyMap;
            }

            @Override
            public void onJoyEvent(NativeEvent none, StandardEvent e) {
                try {
                    if (viewItemEditHandler.lastFocusFunctionItem != null) {
                        viewItemEditHandler.lastFocusFunctionItem.onJoyKeyEvent(e);
                    }
                } catch (Throwable err) {
                    onException(err);
                }
            }

            @Override
            public void onException(Throwable e) {
                form.btnDeviceState.onErr(e);
                onExecuteException("", e);
            }
        });
    }

    private class ViewItemEditHandler implements FunctionEditListener, ChoseListener {
        private FunctionSettingItem lastFocusFunctionItem;

        private void onSelectedConfig(FunctionConfig config) {
            if (config == null) return;//没有在线设备和本地配置
            //log.info("onSelectedConfig={}", config.getName());
            if (confirmNotSaveConfig()) {
                form.cbConfig.setSelectedItem(lastSelectedConfig);
                return;
            }

            //重新读取配置
            String name = config.name;
            config.loadFromFile();
            config.name = name;
            initViewByConfig(config);
            lastSelectedConfig = config;

            boolean existsConfigFile = new File(config.configPath()).exists();
            switchConfigEdited(!existsConfigFile);
        }

        private void initViewByConfig(FunctionConfig config) {
            log.info("config={}", config.name);
            setSubTitle(config.name);

            form.cbExclusive.setSelected(config.exclusive);
            /*
            初始化列表:
            .先删除多余项目,再填充数据
            .界面item比config多1项
             */
            //程序
            int appSize = config.lstApp.size();
            ((GridLayout) form.pApp.getLayout()).setRows(appSize + 1);
            for (int i = form.pApp.getComponentCount() - 1; i >= (appSize + 1); i--) form.pApp.remove(i);
            for (int i = 0; i < appSize; i++) initAppItem(i, config.lstApp.get(i));
            initAppItem(appSize, null);//最后一项是空的

            //功能
            int functionSize = config.lstFunction.size();
            ((GridLayout) form.pFunction.getLayout()).setRows(functionSize + 1);
            for (int i = form.pFunction.getComponentCount() - 1; i >= (functionSize + 1); i--) form.pFunction.remove(i);
            for (int i = 0; i < functionSize; i++) initFunctionItem(i, config.lstFunction.get(i));
            initFunctionItem(functionSize, null);
        }

        /**
         * 初始化[刷新程序]item
         */
        private void initAppItem(int index, String appPath) {
            int count = form.pApp.getComponentCount();
            log.debug("count={}, index={}", count, index);
            FileChooserPanel fileChooser;
            if (index > count - 1) {
                fileChooser = new FileChooserPanel();
                fileChooser.setChooserBtnTxt("选择");
                fileChooser.setHint("双击添加程序");
                fileChooser.setExt("exe");
                fileChooser.setLabel(null);
                fileChooser.setChoseListener(viewItemEditHandler);
                form.pApp.add(fileChooser);
            } else {
                fileChooser = (FileChooserPanel) form.pApp.getComponent(index);
            }
            fileChooser.setSelectedFilePath(appPath);
        }

        private void initFunctionItem(int index, FunctionItem function) {
            int count = form.pFunction.getComponentCount();
            log.debug("count={}, index={}", count, index);
            FunctionSettingItem item;
            if (index > count - 1) {
                item = new FunctionSettingItem();
                item.setEditListener(viewItemEditHandler);
                form.pFunction.add(item);
            } else {
                item = (FunctionSettingItem) form.pFunction.getComponent(index);
            }
            item.setFunction(function);
        }

        @Override
        public void afterChose(String chosePath) {
            log.debug("chosePath={}", chosePath);
            //保证只有最后一项是空白项
            boolean changedItem = false;
            if (chosePath == null) {//删除多余空项
                for (int i = form.pApp.getComponentCount() - 2; i >= 0; i--) {
                    FileChooserPanel fileChooser = (FileChooserPanel) form.pApp.getComponent(i);
                    if (fileChooser.getSelectedFilePath() == null) {
                        form.pApp.remove(i);
                        changedItem = true;
                        break;
                    }
                }
            } else {//保证最后一项是空项
                int currentCount = form.pApp.getComponentCount();
                FileChooserPanel fileChooser = (FileChooserPanel) form.pApp.getComponent(currentCount - 1);
                if (fileChooser.getSelectedFilePath() != null) {
                    initAppItem(currentCount, null);
                    changedItem = true;
                }
            }

            if (changedItem) {
                GridLayout layout = (GridLayout) form.pApp.getLayout();
                layout.setRows(form.pApp.getComponentCount());
                ComponentUtil.afterChangedChild(form.pApp);
            }
            switchConfigEdited(true);
        }

        @Override
        public void afterChangedType(FunctionType type) {
            log.debug("type={}", type);
            //保证只有最后一项是空白项
            boolean changedItem = false;
            if (type == FunctionType.NONE) {//删除多余空项
                for (int i = form.pFunction.getComponentCount() - 2; i >= 0; i--) {
                    FunctionSettingItem item = (FunctionSettingItem) form.pFunction.getComponent(i);
                    if (item.getType() == FunctionType.NONE) {
                        form.pFunction.remove(i);
                        changedItem = true;
                        break;
                    }
                }
            } else {//保证最后一项是空项
                int currentCount = form.pFunction.getComponentCount();
                FunctionSettingItem item = (FunctionSettingItem) form.pFunction.getComponent(currentCount - 1);
                if (item.getType() != FunctionType.NONE) {
                    initFunctionItem(currentCount, null);
                    changedItem = true;
                }
            }
            if (changedItem) {
                GridLayout layout = (GridLayout) form.pFunction.getLayout();
                layout.setRows(form.pFunction.getComponentCount());
                ComponentUtil.afterChangedChild(form.pFunction);
            }
            switchConfigEdited(true);
        }

        @Override
        public void afterEditFunction() {
            switchConfigEdited(true);
        }

        @Override
        public void onFunctionItemFocus(FunctionSettingItem item, boolean isFocus) {
            //log.info("isFocus={}", isFocus);
            lastFocusFunctionItem = isFocus ? item : null;
        }

    }

}
